package hkust.lingx.csit5110.service;

import hkust.lingx.csit5110.util.GaeConstants;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreFailureException;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.gson.Gson;

/**
 * RESTful service is Reference to https://github.com/blueimp/jQuery-File-Upload/issues/425
 * 
 */
@Path("/file")
public class UploadFileService {

  private final BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
  private final BlobInfoFactory blobInfoFactory = new BlobInfoFactory();
  private static List<Map<String, String>> keyIpMapList = new ArrayList<Map<String, String>>();

  /* step 1. get a unique url */

  @GET
  @Path("/url")
  public Response getCallbackUrl() {
    String url = blobstoreService.createUploadUrl("/rest/file");
    return Response.ok(url).build();
  }

  /* step 2. post a file */

  @POST
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  public Response post(@Context HttpServletRequest req, @Context HttpServletResponse res) throws IOException, URISyntaxException {
    Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);
    BlobKey blobKey = blobs.get(GaeConstants.GAE_VOICE);
    String voiceIp = req.getParameter(GaeConstants.GAE_IP);
    Map<String, String> keyIpMap = this.getKeyIpMap(voiceIp);
    if (keyIpMap == null) {
      // not exists,add new record
      keyIpMap = new HashMap<String, String>();      
      keyIpMap.put(GaeConstants.GAE_IP, voiceIp);
      this.setKeyIpMap(keyIpMap, blobKey, req);      
      keyIpMapList.add(keyIpMap);
    } else {
      // already exists,replace the new key,delete old one
      String oldKey = keyIpMap.get(GaeConstants.GAE_KEY);
      blobstoreService.delete(new BlobKey(oldKey));
      this.setKeyIpMap(keyIpMap, blobKey, req);      
    }
    Gson gson = new Gson();
    List targetList = this.getMatchTargetList(keyIpMap);
    String strGson = gson.toJson(targetList, ArrayList.class);
    return Response.ok(strGson).build();
  }
  
  private void setKeyIpMap(Map<String,String> keyIpMap,BlobKey blobKey,HttpServletRequest req){
    keyIpMap.put(GaeConstants.GAE_KEY, blobKey.getKeyString());
    keyIpMap.put(GaeConstants.GAE_LAST_TIME, String.valueOf(new Date().getTime()));
    keyIpMap.put(GaeConstants.GAE_ORG_VOICE, req.getParameter(GaeConstants.GAE_ORG_VOICE));
    keyIpMap.put(GaeConstants.GAE_ORG_EMOTION, req.getParameter(GaeConstants.GAE_ORG_EMOTION));
    keyIpMap.put(GaeConstants.GAE_ORG_GENDER, req.getParameter(GaeConstants.GAE_ORG_GENDER));     
    keyIpMap.put(GaeConstants.GAE_TAR_VOICE, req.getParameter(GaeConstants.GAE_TAR_VOICE));
    keyIpMap.put(GaeConstants.GAE_TAR_EMOTION, req.getParameter(GaeConstants.GAE_TAR_EMOTION));
    keyIpMap.put(GaeConstants.GAE_TAR_GENDER, req.getParameter(GaeConstants.GAE_TAR_GENDER));     
    
  }

  /**
   * iterate the keyIpMapList,get KNN list
   * 
   * @param keyIpMap
   * @return
   */
  private List getMatchTargetList(Map<String, String> keyIpMap) {
    List<Map<String,String>> res=new ArrayList<Map<String,String>>();
    /**
     * filter the user himself first
     */
    for(Map<String,String> map:keyIpMapList){
      if(map==keyIpMap){
        continue;
      }else{
        res.add(map);
      }
    }
    /**
     * KNN algorithm
     */
    int score=0;
    String orgVoice=keyIpMap.get(GaeConstants.GAE_ORG_VOICE);
    String orgEmotion=keyIpMap.get(GaeConstants.GAE_ORG_EMOTION);
    String orgGender=keyIpMap.get(GaeConstants.GAE_ORG_GENDER);
    for(Map<String,String> map:res){
      String tarVoice=keyIpMap.get(GaeConstants.GAE_TAR_VOICE);
      String tarEmotion=keyIpMap.get(GaeConstants.GAE_TAR_EMOTION);
      String tarGender=keyIpMap.get(GaeConstants.GAE_TAR_GENDER);
      score= (tarVoice.charAt(0)-orgVoice.charAt(0))
            +(tarEmotion.charAt(0)-orgEmotion.charAt(0))
            +(tarGender.charAt(0)-orgGender.charAt(0));
      map.put(GaeConstants.GAE_SCORE, String.valueOf(score));
    }
    Collections.sort(res, new Comparator<Map<String,String>>(){
      @Override
      public int compare(Map<String,String> map1, Map<String,String> map2) {
        int score1=Integer.valueOf(map1.get(GaeConstants.GAE_SCORE));
        int score2=Integer.valueOf(map2.get(GaeConstants.GAE_SCORE));
        if(score1>score2){
          return -1;
        }else{
          return 1;
        }
      }
    });
    return res;
  }

  private Map<String, String> getKeyIpMap(String ip) {
    for (Map<String, String> map : keyIpMapList) {
      String gaeIp = map.get(GaeConstants.GAE_IP);
      if (ip.equalsIgnoreCase(gaeIp)) {
        return map;
      }
    }
    return null;
  }

  /* step 3. download the file */
  @GET
  @Path("/{key}")
  public Response serve(@PathParam("key") String key, @Context HttpServletResponse response) throws IOException {
    BlobKey blobKey = new BlobKey(key);
    final BlobInfo blobInfo = blobInfoFactory.loadBlobInfo(blobKey);
    response.setHeader("Content-Disposition", "attachment; filename=" + blobInfo.getFilename());
    BlobstoreServiceFactory.getBlobstoreService().serve(blobKey, response);
    return Response.ok().build();
  }

  /* step 4. delete the file */
  @DELETE
  @Path("/{key}")
  public Response delete(@PathParam("key") String key) {
    Status status;
    try {
      blobstoreService.delete(new BlobKey(key));
      status = Status.OK;
    } catch (BlobstoreFailureException bfe) {
      status = Status.NOT_FOUND;
    }
    return Response.status(status).build();
  }

  @GET
  @Path("heartbeat/{ip}")
  public Response heartbeat(@PathParam("ip") String ip, @Context HttpServletResponse response) throws IOException {
    Map<String, String> keyIpMap = this.getKeyIpMap(ip);
    if (keyIpMap != null) {
      keyIpMap.put(GaeConstants.GAE_LAST_TIME, String.valueOf(new Date().getTime()));
    }
    return Response.ok().build();
  }

  /*
   * step 5. redirected to the meta info
   * 
   * @GET
   * 
   * @Path("/{key}/meta") public Response redirect(@PathParam("key") String key)
   * throws IOException { BlobKey blobKey = new BlobKey(key);
   * 
   * BlobInfo info = blobInfoFactory.loadBlobInfo(blobKey);
   * 
   * String name = info.getFilename(); long size = info.getSize(); String url =
   * "/rest/file/" + key; FileMeta meta = new FileMeta(name, size, url);
   * 
   * List<FileMeta> metas = Lists.newArrayList(meta);
   * GenericEntity<List<FileMeta>> entity = new
   * GenericEntity<List<FileMeta>>(metas) {}; return
   * Response.ok(entity).build(); }
   */
}
